﻿Imports Runa.Common.Helper
Imports System.Collections
Imports System.Collections.Generic

Namespace Collections

    Public Class SortedList(Of T As IComparable(Of T))
        Inherits List(Of T)

        Public Property Comparer() As Comparer(Of T) = System.Collections.Generic.Comparer(Of T).Default

        Public Sub New()
            MyBase.New()
        End Sub

        Public Sub New(ByVal cmper As Comparer(Of T))
            MyBase.New()
            Me.Comparer = cmper
        End Sub

        Public Overloads Sub Add(ByVal item As T)
            MyBase.Add(item)
            Me.Sort(Me.Comparer)
        End Sub

        Public Overloads Sub AddRange(ByVal collection As System.Collections.Generic.IEnumerable(Of T))
            MyBase.AddRange(collection)
            Me.Sort(Me.Comparer)
        End Sub

        Public Overloads Sub Insert(index As Integer, item As T)
            MyBase.Insert(index, item)
            Me.Sort(Me.Comparer)
        End Sub

        Public Overloads Sub InsertRange(index As Integer, collection As System.Collections.Generic.IEnumerable(Of T))
            MyBase.InsertRange(index, collection)
            Me.Sort(Me.Comparer)
        End Sub

        Public Overloads Function Remove(item As T) As Boolean
            Dim result = MyBase.Remove(item)
            Me.Sort(Me.Comparer)
            Return result
        End Function

        Public Overloads Function RemoveAll(match As System.Predicate(Of T)) As Integer
            Dim result = MyBase.RemoveAll(match)
            Me.Sort(Me.Comparer)
            Return result
        End Function

        Public Overloads Sub RemoveAt(index As Integer)
            MyBase.RemoveAt(index)
            Me.Sort(Me.Comparer)
        End Sub

        Public Overloads Sub RemoveRange(index As Integer, count As Integer)
            MyBase.RemoveRange(index, count)
            Me.Sort(Me.Comparer)
        End Sub

        Private Overloads Sub Reverse()
            MyBase.Reverse()
        End Sub

        Private Overloads Sub Reverse(index As Integer, count As Integer)
            MyBase.Reverse(index, count)
        End Sub

    End Class

    Public Class SortedList(Of TKey As IComparable(Of TKey), TValue)
        Implements IDictionary(Of TKey, TValue)

        Private m_collection As New SortedList(Of CompareableObject(Of KeyValuePair(Of TKey, TValue)))

        Public Sub Add(item As KeyValuePair(Of TKey, TValue)) Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Add
            If Me.m_collection.Contains(Function(collectionItem) collectionItem.Value.Key.Equals(item.Key)) Then
                Throw New ArgumentException("Duplicated key found.")
            Else
                Me.m_collection.Add(New CompareableObject(Of KeyValuePair(Of TKey, TValue))(item, AddressOf CompareTo))
            End If
        End Sub

        Public Sub Clear() Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Clear
            Me.m_collection.Clear()
        End Sub

        Public Function Contains(item As KeyValuePair(Of TKey, TValue)) As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Contains
            Return Me.m_collection.Contains(Function(collectionItem) collectionItem.Value.Equals(item))
        End Function

        Public Sub CopyTo(array() As KeyValuePair(Of TKey, TValue), arrayIndex As Integer) Implements ICollection(Of KeyValuePair(Of TKey, TValue)).CopyTo
            Dim lst = (From item In Me.m_collection Select item.Value).ToArray()
            Dim i As Integer = arrayIndex
            For Each collectionItem In Me.m_collection
                If i <= array.Length - 1 Then
                    array(i) = collectionItem.Value
                    i += 1
                Else
                    Throw New IndexOutOfRangeException()
                End If
            Next
        End Sub

        Public ReadOnly Property Count() As Integer Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Count
            Get
                Return Me.m_collection.Count
            End Get
        End Property

        Public ReadOnly Property IsReadOnly() As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).IsReadOnly
            Get
                Return False
            End Get
        End Property

        Public Function Remove(item As KeyValuePair(Of TKey, TValue)) As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Remove
            If Me.m_collection.Contains(Function(collectionItem) collectionItem.Value.Key.Equals(item.Key)) Then
                Return Me.m_collection.RemoveFirstIf(Function(element) element.Value.Equals(item))
            Else
                Throw New ArgumentException("The specified item is not in this collection.")
            End If
        End Function

        Public Sub RemoveAt(ByVal index As Integer)
            Me.m_collection.RemoveAt(index)
        End Sub

        Public Sub Add(key As TKey, value As TValue) Implements IDictionary(Of TKey, TValue).Add
            Me.Add(New KeyValuePair(Of TKey, TValue)(key, value))
        End Sub

        Public Function ContainsKey(key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).ContainsKey
            Return Me.m_collection.Contains(Function(collectionItem) collectionItem.Value.Key.Equals(key))
        End Function

        Default Public Property Item(key As TKey) As TValue Implements IDictionary(Of TKey, TValue).Item
            Get
                Dim element = Me.m_collection.FirstOrDefault(Function(collectionItem) collectionItem.Value.Key.Equals(key))
                If element Is Nothing Then
                    Throw New ArgumentException("The given key is not existed.")
                Else
                    Return element.Value.Value
                End If
            End Get
            Set(value As TValue)
                Dim element = Me.m_collection.FirstOrDefault(Function(collectionItem) collectionItem.Value.Key.Equals(key))
                If element Is Nothing Then
                    Me.Add(key, value)
                Else
                    Me.Remove(key)
                    Me.Add(key, value)
                End If
            End Set
        End Property

        Public ReadOnly Property Keys() As ICollection(Of TKey) Implements IDictionary(Of TKey, TValue).Keys
            Get
                Return (From item In Me.m_collection Select item.Value.Key).ToList()
            End Get
        End Property

        Public Function Remove(key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).Remove
            If Me.m_collection.Contains(Function(collectionItem) collectionItem.Value.Key.Equals(key)) Then
                Return Me.m_collection.RemoveFirstIf(Function(collectionItem) collectionItem.Value.Key.Equals(key))
            Else
                Throw New ArgumentException("The specified item is not in this collection.")
            End If
        End Function

        Public Function TryGetValue(key As TKey, ByRef value As TValue) As Boolean Implements IDictionary(Of TKey, TValue).TryGetValue
            If Me.m_collection.Contains(Function(collectionItem) collectionItem.Value.Key.Equals(key)) Then
                value = Me.Item(key)
                Return True
            Else
                Return False
            End If
        End Function

        Public ReadOnly Property Values() As ICollection(Of TValue) Implements IDictionary(Of TKey, TValue).Values
            Get
                Return (From item In Me.m_collection Select item.Value.Value).ToList()
            End Get
        End Property

        Public Function GetEnumerator() As IEnumerator(Of KeyValuePair(Of TKey, TValue)) Implements IEnumerable(Of KeyValuePair(Of TKey, TValue)).GetEnumerator
            Return Me.Values.GetEnumerator()
        End Function

        Private Function GetEnumerator_() As IEnumerator Implements IEnumerable.GetEnumerator
            Return Me.GetEnumerator()
        End Function

        Private Shared Function CompareTo(ByVal x As KeyValuePair(Of TKey, TValue), ByVal y As KeyValuePair(Of TKey, TValue)) As Integer
            Dim comparison = Comparer(Of TKey).Default
            Return comparison.Compare(x.Key, y.Key)
        End Function

    End Class

End Namespace